#include "clib_stack_sock.h"
#include "clib_stack_conf.h"


int32_t 
clib_getsockopt(clib_sock_t* sock,
                int32_t sockid,
                int32_t level,
                int32_t optname,
                void *optval,
                socklen_t *optlen)
{
    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(sock->sock_type == socket_type_unknow) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(level == SOL_SOCKET) {
        if(optname == SO_ERROR) {
            if(sock->sock_type == socket_type_sock_tcp) {
                /** TODO  **/
                return 0;
            }
        }

    }

    errno = ENOSYS;
	return -1;
}


int32_t 
clib_setsockopt(clib_sock_t* sock,
                int32_t sockid,
                int32_t level,
		        int32_t optname,
                const void *optval,
                socklen_t optlen)
{

    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(sock->sock_type == socket_type_unknow) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    return 0;
}


int32_t 
clib_setsock_nonblock(clib_sock_t* sock, int32_t sockid)
{
    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(sock->sock_type == socket_type_unknow) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    sock->opts |= socket_opts_unblock;
    return 0;
}

/* mtcp_socket_ioctl: similar to ioctl, 
   but only FIONREAD is supported currently */
int32_t 
clib_socket_ioctl(clib_sock_t* sock, int32_t sockid, int32_t request, void *argp)
{
    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if( !(sock->sock_type == socket_type_sock_tcp 
       || sock->sock_type == socket_type_listen)) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(!argp) {
        errno = EFAULT;
        return -1;
    }

    if(request == FIONREAD) {
        tcp_stream_t* stream = &sock->tcp_stream;
        if(stream->recv_buffer) {
            tcp_buffer_t* buf = stream->recv_buffer;
            *(int32_t *)argp = (buf->value_len - (buf->frag_len + buf->ready_len));
        } else {
            *(int32_t *)argp = 0;
        }
    } else if(request == FIONBIO) {
        int32_t arg = *(int32_t *)argp;
		if (arg != 0) {
            return clib_setsock_nonblock(sock, sockid);
        }	
    } else {
		errno = EINVAL;
		return -1;
	}

	return 0;
}


int32_t 
clib_socket(clib_sock_t* sock, int32_t domain, int32_t type, int32_t protocol)
{


}


int32_t 
clib_bind(clib_sock_t* sock, 
          int32_t sockid, 
		  const struct sockaddr *addr,
          socklen_t addrlen) 
{
    struct sockaddr_in6 *in6 = NULL;

    struct sockaddr_in  *in4 = NULL;
    
    if(addrlen == sizeof(struct sockaddr_in6)) {
        in6 = (struct sockaddr_in6 *)addr;
    } else {
        in4 = (struct sockaddr_in  *)addr;
    }

   if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if( !(sock->sock_type == socket_type_sock_tcp 
       || sock->sock_type == socket_type_listen)) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(in4) {
        if(in4->sin_family != AF_INET || (addrlen == sizeof(struct sockaddr_in))) {
            clib_stack_trace("Invalid addr socket id: %d\n", sockid);
            errno = EINVAL;
            return -1;
        }
        sock->opts |= socket_opts_bind;
        sock->ipv4 = 1;
        sock->ipv4_tuple.l_addr = in4->sin_addr.s_addr;
        sock->ipv4_tuple.l_port = in4->sin_port;
    } else {
        if(in6->sin6_family != AF_INET6 || (addrlen == sizeof(struct sockaddr_in6))) {
            clib_stack_trace("Invalid addr socket id: %d\n", sockid);
            errno = EINVAL;
            return -1;
        }
        sock->opts |= socket_opts_bind;
        sock->ipv4 = 1;
        memcpy(&sock->ipv6_tuple.l_addr, &in6->sin6_addr, sizeof(struct in6_addr));
        sock->ipv6_tuple.l_port = in6->sin6_port;
    }

    return 0;
}


int32_t 
clib_listen(clib_sock_t* sock, int32_t sockid, int32_t backlog)
{
    tcp_listen_t* listen;

    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if (backlog <= 0 || backlog > stack_cfg.sock_cnt) {
		errno = EINVAL;
        clib_stack_trace("Invalid backlog count: %d\n", backlog);
		return -1;
	}

    if(sock->sock_type != socket_type_sock_tcp) {
        clib_stack_trace("Invalid socket type  Mismatch ,socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    sock->sock_type = socket_type_listen;

    /** TODO 检测端口是否在已存在 **/
    // if() {
    //     errno = EADDRINUSE;
	// 	return -1;
    // }
    listen = &sock->tcp_listen;

    listen->sock_id = sockid;
    listen->backlog = backlog;
    listen->accept_n = 0;
    clib_queue_init(&listen->accept);

    /** TODO 插入监听队列 **/
    return 0;
}

int32_t 
clib_accept(clib_sock_t* sock, int32_t sockid, struct sockaddr *addr, socklen_t *addrlen)
{
    tcp_listen_t* listen;
    clib_sock_t* sk;
    struct sockaddr_in6 *in6 = NULL;
    struct sockaddr_in  *in4 = NULL;

    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(sock->sock_type != socket_type_listen) {
        clib_stack_trace("Invalid socket type, socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    listen = &sock->tcp_listen;

    if(listen->accept_n > 0) {
        clib_spinlock_lock(&listen->lock);
        clib_queue_t* node = clib_queue_next(&listen->accept);
        clib_queue_remove(node);
        listen->accept_n--;
        clib_spinlock_unlock(&listen->lock);

        sk = clib_queue_data(node,clib_sock_t,list);

        if(addr && addrlen) {
            if(sock->ipv4) {
                in4 = (struct sockaddr_in* )addr;
                in4->sin_family = AF_INET;
                in4->sin_port   = sk->ipv4_tuple.l_port;
                in4->sin_addr.s_addr = sk->ipv4_tuple.l_addr;
                *addrlen = sizeof(struct sockaddr_in);
            } else {
                in6 = (struct sockaddr_in6*)addr;
                in6->sin6_family = AF_INET6;
                in6->sin6_port   = sk->ipv6_tuple.l_port;
                memcpy(&in6->sin6_addr,&sk->ipv6_tuple.l_addr,sizeof(struct in6_addr) );
                *addrlen = sizeof(struct sockaddr_in6);
            }
        }

        return sk->sock_id;
    }

    return -1;
}

// int32_t 
// clib_init_rss(mctx_t mctx, in_addr_t saddr_base, int32_t num_addr, 
// 		in_addr_t daddr, in_addr_t dport);

int32_t 
clib_connect(clib_sock_t* sock, 
             int32_t sockid, 
		     const struct sockaddr *addr,
             socklen_t addrlen)
{
	tcp_stream_t *stream;
    struct sockaddr_in6 *in6 = NULL;
    struct sockaddr_in  *in4 = NULL;

    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(sock->sock_type != socket_type_sock_tcp) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = ENOTSOCK;
        return -1;
    }

    if (!addr) {
		clib_stack_trace("Socket %d: empty address!\n", sockid);
		errno = EFAULT;
		return -1;
	}

    if(addrlen == sizeof(struct sockaddr_in)) {
        sock->ipv4 = 1;
        in4 = (struct sockaddr_in*)addr;
        if(in4->sin_family != AF_INET) {
            clib_stack_trace("ipv4 Socket %d: invalid argument!\n", sockid);
            errno = EAFNOSUPPORT;
            return -1;
        }
        sock->ipv4_tuple.r_port = in4->sin_port;
        sock->ipv4_tuple.r_addr = in4->sin_addr.s_addr;
    } else if(addrlen == sizeof(struct sockaddr_in6)) {
        sock->ipv4 = 0;
        in6 = (struct sockaddr_in6*)addr;
        if(in6->sin6_family != AF_INET6) {
            clib_stack_trace("ipv6 Socket %d: invalid argument!\n", sockid);
            errno = EAFNOSUPPORT;
            return -1;
        }
        memcpy(&sock->ipv6_tuple.r_addr, &in6->sin6_addr, sizeof(struct in6_addr));
        sock->ipv6_tuple.r_port = in6->sin6_port;
    } else {
        clib_stack_trace("invalid argument!\n");
        errno = EINVAL;
        return -1;
    }

    if(!(sock->opts & socket_opts_bind)) {
        /** TODO 查找空闲链表 **/
    }

    if(sock->ipv4) {
        /** TODO 插入流 table**/

    } else {

    }

    /** TODO 发送syn数据包 **/

    return 0;
}


int32_t 
clib_close(clib_sock_t* sock, int32_t sockid)
{

    return 0;
}


int32_t
clib_getsockname(clib_sock_t* sock,
                 int32_t sockid,
                 struct sockaddr *addr,
                 socklen_t *addrlen)
{
    struct sockaddr_in6 *in6 = NULL;
    struct sockaddr_in  *in4 = NULL;

    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(!(sock->sock_type == socket_type_sock_tcp 
       || sock->sock_type == socket_type_listen)) 
    {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
		errno = ENOTSOCK;
		return -1;
    }

	if ( !addr || !addrlen <= 0 || *addrlen <= 0) {
		clib_stack_trace("Invalid addrlen: %d\n", *addrlen);
		errno = EINVAL;
		return -1;
	}

    if(sock->ipv4) {
        in4 = (struct sockaddr_in* )addr;
        in4->sin_family = PF_INET;
        in4->sin_port = sock->ipv4_tuple.l_port;
        in4->sin_addr.s_addr = sock->ipv4_tuple.l_addr;
        *addrlen = sizeof(struct sockaddr_in);
    } else {
        in6 = (struct sockaddr_in6*)addr;
        in6->sin6_family = PF_INET6;
        in6->sin6_port   = sock->ipv6_tuple.l_port;
        memcpy(&in6->sin6_addr,&sock->ipv6_tuple.l_addr,sizeof(struct in6_addr));
        *addrlen = sizeof(struct sockaddr_in6);
    }

    return 0;
}


int32_t
clib_getpeername(clib_sock_t* sock,
                 int32_t sockid,
                 struct sockaddr *addr,
		         socklen_t *addrlen)
{
    struct sockaddr_in6 *in6 = NULL;
    struct sockaddr_in  *in4 = NULL;

    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(!(sock->sock_type == socket_type_sock_tcp 
       || sock->sock_type == socket_type_listen)) 
    {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
		errno = ENOTSOCK;
		return -1;
    }

	if ( !addr || *addrlen <= 0) {
		clib_stack_trace("Invalid addrlen: %d\n", *addrlen);
		errno = EINVAL;
		return -1;
	}

    if(sock->ipv4) {
        in4 = (struct sockaddr_in* )addr;
        in4->sin_family = PF_INET;
        in4->sin_port = sock->ipv4_tuple.r_port;
        in4->sin_addr.s_addr = sock->ipv4_tuple.r_addr;
        *addrlen = sizeof(struct sockaddr_in);
    } else {
        in6 = (struct sockaddr_in6*)addr;
        in6->sin6_family = PF_INET6;
        in6->sin6_port   = sock->ipv6_tuple.r_port;
        memcpy(&in6->sin6_addr,&sock->ipv6_tuple.r_addr,sizeof(struct in6_addr));
        *addrlen = sizeof(struct sockaddr_in6);
    }

    return 0;
}


inline ssize_t
clib_read(clib_sock_t* sock, int32_t sockid, char *buf, size_t len)
{
    return clib_recv(sock, sockid, buf, len, 0);
}


ssize_t
clib_recv(clib_sock_t* sock, int32_t sockid, char *buf, size_t len, int32_t flags)
{
    tcp_stream_t *stream;
    i32_t         r_len = 0;
    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(sock->sock_type != socket_type_sock_tcp) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
		errno = ENOTSOCK;
		return -1;
    }

    /** TODO 兼容UDP **/
    stream = &sock->tcp_stream;
    if(stream->recv_buffer) {
        if(!flags) {
            r_len = clib_tcp_buffer_recv_get_data(stream->recv_buffer,buf,(size_t)len);
        } else {
            /** TODO 获取buf不确认长度 **/

        }
    }
    /** TODO 判断事件是否完成 **/
    return r_len;
}

ssize_t
clib_write(clib_sock_t* sock, int32_t sockid, const char *buf, size_t len)
{
    tcp_stream_t *stream;
    i32_t         s_len = 0;
    if(sockid < 0 || sockid > stack_cfg.sock_cnt || !sock) {
        clib_stack_trace("Invalid socket id: %d\n", sockid);
        errno = EBADF;
        return -1;
    }

    if(!buf || !len) {
        clib_stack_trace("Invalid value socket id: %d\n", sockid);
        errno = EINVAL;
        return -1;
    }
    stream = &sock->tcp_stream;

    if(stream->send_buffer) {
        s_len = clib_tcp_buffer_send_put_data(stream->send_buffer, buf, len);
        if(!s_len) {
            return -1;
        }
        return s_len;
    }

    return -1;
}